package com.handsome.controller;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.handsome.controller.consts.CommonConst;
import com.handsome.entity.HsWidget;
import com.handsome.utils.StrUtil;
import com.handsome.service.WidgetService;
import com.handsome.utils.HttpUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.*;

@RequestMapping("/admin")
public abstract class BaseController {


    @Autowired
    private WidgetService widgetService;

    public static void writeJson(Map<String, Object> map, HttpServletResponse response) {
        PrintWriter out = null;
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            out = response.getWriter();
            out.write(new ObjectMapper().writeValueAsString(map));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * 登录异常拦截
     *
     * @param request  请求
     * @param response 返回
     */
    @ExceptionHandler({UnauthenticatedException.class, AuthenticationException.class})
    public void authenticationException(HttpServletRequest request, HttpServletResponse response) {
        // 输出JSON
        Map<String, Object> map = new HashMap<>();
        map.put("resultCode", 0);
        map.put("message", "未登录");
        writeJson(map, response);
    }

    /**
     * 权限异常拦截
     *
     * @param request  请求
     * @param response 返回
     */
    @ExceptionHandler({UnauthorizedException.class, AuthorizationException.class})
    public void authorizationException(HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", -1);
        map.put("message", "无权限操作");
        writeJson(map, response);
    }


    /**
     * 自动注入表单
     *
     * @param model 视图
     */
    @ModelAttribute
    public void InjectionForm(Model model) {
        // 获取当前请求对象
        HttpServletRequest request = HttpUtil.getRequest();
        // 判断当前页面是否需要注入表单 (build_form 需要注入 |  preview 表单预览)
        if (!StringUtils.isBlank(request.getParameter("build_form")) && StringUtils.isBlank(request.getParameter("preview"))) {

            // 根据表单id 获取动态表单信息 (form_id 动态表单id)
            HsWidget formData = widgetService.getFormById(Integer.valueOf(request.getParameter("form_id")));
            // 获取表单字段表信息
            String entity = formData.getMappingEntity();
            // 动态获取 表信息
            HashMap<String, Object> entityData = widgetService.buildGetEntitySql(entity, Integer.valueOf(request.getParameter("id")));
            // 下划线转成驼峰 映射entity
            HashMap<String, Object> humpData = new HashMap<>();
            entityData.forEach((k, v) -> humpData.put(StrUtil.camelName(k), v));
            // 获取当前的uri信息
            String uri = request.getRequestURI();
            // add edit 根据uri 获取类型
            String actionType = uri.substring(uri.lastIndexOf("/") + 1);
            request.setAttribute(CommonConst.ENTITY_DATA_KEY, humpData);
            try {
                // 把表单字段自动转成List
                List fieldList = new ObjectMapper().readValue(formData.getFields(), ArrayList.class);
                WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
                // 保证表单值反射调用一次
                boolean off = true;
                for (Object e : fieldList) {
                    HashMap<String, Object> formFieldItem = (HashMap<String, Object>) e;
                    // 表单类型等于编辑  则自动填充数据 (value值)
                    if (actionType.equals(CommonConst.ACTION_TYPE_EDIT)) {
                        Object tableFieldNameValue = humpData.get(formFieldItem.get("name"));
                        formFieldItem.put("value", tableFieldNameValue);
                    }
                    // 该字段是否需要放射调用
                    if (formFieldItem.get("call").equals("true")) {
                            this.reflectionCall(request, wac, formFieldItem);
                            off = false;
                    }
                }
                // 向视图注入数据
                HashMap<String, Object> viewData = new HashMap<>();
                // 表单字段
                viewData.put("widgets", fieldList);
                // 是否提交
                viewData.put("submit", true);
                // 表单信息
                viewData.put("formData", formData);
                // 操作按钮
                viewData.put("btnLabel", new String(request.getParameter("btn_label").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
                model.addAllAttributes(viewData);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 反射调用获取数据 并映射到表单
     *
     * @param wac 当前
     * @param map 应属的数据
     */
    protected void reflectionCall(HttpServletRequest request, WebApplicationContext wac, HashMap<String, Object> map) {
        String methodName = (String) map.get("method_name");
        String className = (String) map.get("class_name");
        try {
            Class mappingWidgetClass = Class.forName(CommonConst.SOURCE_PACKAGE + '.' + className);
            Class springMappingWidgetClass = wac.getBean(mappingWidgetClass).getClass();
            Method method = springMappingWidgetClass.getDeclaredMethod(methodName, HttpServletRequest.class, HashMap.class);
            Map mappingData = (Map) method.invoke(wac.getBean(mappingWidgetClass), request, map);
            if (mappingData != null) {
                System.out.println(map.get("type"));
                map.put("data", mappingData.get(map.get("type")));
            }
        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e1) {
            e1.printStackTrace();
        }
    }


}
